import backtrader as bt
import logging
from datetime import datetime
from .DbOprate import DbOprate

class TDXFunctions:
    """通达信常用函数工具类"""
    
    @staticmethod
    def LLV(data, period):
        """最低值函数 - 返回指定周期内的最小值"""
        if len(data) < period:
            return min(data) if len(data) > 0 else 0
        return min(data.get(ago=0, size=period))
    
    @staticmethod
    def HHV(data, period):
        """最高值函数 - 返回指定周期内的最大值"""
        if len(data) < period:
            return max(data) if len(data) > 0 else 0
        return max(data.get(ago=0, size=period))
    
    @staticmethod
    def CROSS_simple(a_prev, a_curr, b_prev, b_curr):
        """简化版交叉函数，用于数值比较"""
        return (a_prev < b_prev) and (a_curr >= b_curr)
    
    @staticmethod
    def FILTER(signals, period):
        """
        过滤函数 - 在指定周期内，同一个条件只能成立一次
        返回过滤后的完整信号列表
        """
        if len(signals) == 0:
            return []
        
        filtered_signals = []
        for i in range(len(signals)):
            if not signals[i]:
                filtered_signals.append(False)
                continue
            
            # 检查过去period周期内是否有其他True信号
            start_idx = max(0, i - period)
            recent_signals = signals[start_idx:i]
            if sum(recent_signals) == 0:
                filtered_signals.append(True)
            else:
                filtered_signals.append(False)
        
        return filtered_signals
    
    @staticmethod
    def BARSLAST(condition_signals):
        """
        上一次条件成立距今的周期数
        返回距离最近一次True信号的周期数（从0开始）
        """
        if len(condition_signals) == 0:
            return 0
        
        # 从后往前查找最近的True信号
        for i in range(len(condition_signals)):
            idx = len(condition_signals) - 1 - i
            if condition_signals[idx]:
                return i
        return len(condition_signals) - 1  # 如果从未找到，则返回最大距离
    
    @staticmethod
    def REF(data, periods):
        """
        引用函数 - 引用若干周期前的数据
        periods: 周期数（正数表示往前推）
        """
        if len(data) <= periods or periods < 0:
            return data[0] if len(data) > 0 else 0
        return data[-periods] if periods > 0 else data[0]


class TDXLowPointStrategy(bt.Strategy):
    """
    通达信低点策略
    D1:= 20;
    D2:= 10;
    X_13: CLOSE=LLV(CLOSE,D1);{是否20日最低收盘价}
    X_13_1: CROSS(0.8,X_13);
    X_13_2: FILTER(CROSS(0.8,X_13),D2);
    X_14: BARSLAST(FILTER(CROSS(0.8,X_13),D2))+1;{距离最近一次"股价从创阶段新低后反弹"的信号，已经过去了多少根K线}
    DDD1: REF(LOW,X_14);{短期低点开始画虚线}
    X_15: X_14=1;
    DRAWICON(X_15,9,1);{阶段性低点第二天出现买点（确定性不足）1是向上红箭头};
    """
    params = (
        ('d1', 20),      # 20日周期
        ('d2', 10),      # 10日周期 - 过滤周期
    )

    def __init__(self):
        self.data_close = self.datas[0].close
        self.data_low = self.datas[0].low
        self.tdx = TDXFunctions()

        # 存储历史数据
        self.x_13_history = []
        self.x_13_1_history = []
        self.x_13_2_history = []
        self.x_14_history = []
        self.ddd1_history = []
        self.x_15_history = []

        # 设置日志
        self.setup_logging()

    def setup_logging(self):
        """设置日志配置"""
        self.logger = logging.getLogger(f'TDXLowPointStrategy_{self.datas[0]._name}')
        self.logger.setLevel(logging.INFO)
        
        # 创建文件处理器
        log_filename = f'tdx_lowpoint_strategy_{self.datas[0]._name}_{datetime.now().strftime("%Y%m%d")}.log'
        file_handler = logging.FileHandler(log_filename, encoding='utf-8')
        file_handler.setLevel(logging.INFO)
        
        # 创建格式器
        formatter = logging.Formatter('%(asctime)s - %(message)s')
        file_handler.setFormatter(formatter)
        
        # 添加处理器
        if not self.logger.handlers:
            self.logger.addHandler(file_handler)

    def log_daily_params(self, params_dict):
        """记录每日参数到日志文件"""
        log_msg = "\n" + "="*80 + "\n"
        log_msg += f"日期: {params_dict['date']}\n"
        log_msg += f"股票代码: {params_dict['stock_code']}\n"
        log_msg += "-"*80 + "\n"
        log_msg += f"CLOSE: {params_dict['close']:.4f}\n"
        log_msg += f"LOW: {params_dict['low']:.4f}\n"
        log_msg += f"LLV(CLOSE,{self.p.d1}): {params_dict['llv_close']:.4f}\n"
        log_msg += f"X_13 (CLOSE=LLV(CLOSE,{self.p.d1})): {params_dict['x_13']} ({params_dict['x_13_numeric']})\n"
        log_msg += f"X_13_1 (CROSS(0.8,X_13)): {params_dict['x_13_1']}\n"
        log_msg += f"X_13_2 (FILTER(CROSS(0.8,X_13),{self.p.d2})): {params_dict['x_13_2']}\n"
        log_msg += f"X_14 (BARSLAST(FILTER(...))+1): {params_dict['x_14']}\n"
        log_msg += f"DDD1 (REF(LOW,X_14)): {params_dict['ddd1']:.4f}\n"
        log_msg += f"X_15 (X_14=1): {params_dict['x_15']}\n"
        log_msg += "="*80 + "\n"
        
        self.logger.info(log_msg)
        
        # 如果有买入信号，额外记录
        if params_dict['x_15']:
            signal_msg = f"\n*** 买入信号 *** 价格: {params_dict['close']:.4f} ***\n"
            self.logger.info(signal_msg)

    def next(self):
        current_date = self.datas[0].datetime.date(0)
        current_price = float(self.data_close[0])
        current_low = float(self.data_low[0])

        # ========== 严格按照通达信公式顺序计算 ==========
        
        # X_13: CLOSE=LLV(CLOSE,D1) - 是否20日最低收盘价
        llv_value = self.tdx.LLV(self.data_close, self.p.d1)
        x_13_value = abs(current_price - llv_value) < 0.0001
        x_13_numeric = int(x_13_value)
        self.x_13_history.append(x_13_numeric)
        
        # X_13_1: CROSS(0.8,X_13) - 检测 X_13 从 0 变为 1
        x_13_1_value = False
        if len(self.x_13_history) >= 2:
            x_13_prev = self.x_13_history[-2]
            x_13_curr = self.x_13_history[-1]
            # 模拟 CROSS(0.8, X_13) - 即 X_13 从 0 变为 1
            x_13_1_value = self.tdx.CROSS_simple(0.8, 0.8, x_13_prev, x_13_curr)
        self.x_13_1_history.append(x_13_1_value)
        
        # X_13_2: FILTER(CROSS(0.8,X_13),D2)
        x_13_2_value = False
        if len(self.x_13_1_history) > 0:
            # 应用FILTER函数
            all_filtered = self.tdx.FILTER(self.x_13_1_history, self.p.d2)
            x_13_2_value = all_filtered[-1] if len(all_filtered) > 0 else False
        self.x_13_2_history.append(x_13_2_value)
        
        # X_14: BARSLAST(FILTER(CROSS(0.8,X_13),D2))+1
        barslast_value = self.tdx.BARSLAST(self.x_13_2_history)
        x_14_value = barslast_value + 1
        self.x_14_history.append(x_14_value)
        
        # DDD1: REF(LOW,X_14)
        ddd1_value = self.tdx.REF(self.data_low, x_14_value)
        self.ddd1_history.append(ddd1_value)
        
        # X_15: X_14=1
        x_15_value = (x_14_value == 1)
        self.x_15_history.append(x_15_value)
        
        # ========== 记录所有参数到日志 ==========
        daily_params = {
            'date': current_date.isoformat(),
            'stock_code': self.datas[0]._name,
            'close': current_price,
            'low': current_low,
            'llv_close': llv_value,
            'x_13': x_13_value,
            'x_13_numeric': x_13_numeric,
            'x_13_1': x_13_1_value,
            'x_13_2': x_13_2_value,
            'x_14': x_14_value,
            'ddd1': ddd1_value,
            'x_15': x_15_value
        }
        
        self.log_daily_params(daily_params)
        
        # ========== DRAWICON(X_15,9,1) ==========
        if x_15_value:
            print(f'{current_date} - *** DRAWICON: 阶段性低点买点信号 *** 价格: {current_price:.4f}')
            
            # 存储信号到数据库
            try:
                result = {
                    'strategy': 'TDXLowPointStrategy',
                    'stock_code': self.datas[0]._name,
                    'op_act': 'buy_signal',
                    'op_price': current_price,
                    'op_num': 100,
                    'op_comm': 0.0,
                    'op_dt': current_date.isoformat(),
                    'reason': '阶段性低点买点信号',
                    'x_14_value': float(x_14_value),
                    'ddd1_value': float(ddd1_value),
                    'detail_info': f'CLOSE={current_price}, X_13={x_13_numeric}, X_14={x_14_value}'
                }
                DbOprate.save_result_to_mysql(result)
                print(f'{current_date} - *** 信号已存入数据库 ***')
            except Exception as e:
                print(f'{current_date} - *** 数据库存储失败: {str(e)} ***')

    def stop(self):
        print(f'=== TDX低点策略执行完成 ===')
        print(f'日志文件已保存到: tdx_lowpoint_strategy_{self.datas[0]._name}_*.log')
